// Parse.cpp: implementation of the CParse class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "8085.h"
#include "constants.h"
#include "MyException.h"
#include "MyTypedef.h"
#include "8085Object.h"

#include "Parse.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

#define IS_NUMBER(x) ((x==HEX) || (x==OCTAL) || (x==DECIMAL) || (x==BINARY))

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CParse::CParse() : Lex()
{
    Init();
}

CParse::CParse(C8085Object *obj) : Lex()
{
    Init();
    Set8085Object(obj);
}

CParse::~CParse()
{
	Reset();
    Close();
    delete m_szTemporary;
}

//////////////////////////////////////////////////////////////////////
// Implementation
//////////////////////////////////////////////////////////////////////
void CParse::Set8085Object(C8085Object * obj)
{
    m_p8085Object = obj;
    //m_InstrTable.Set8085Object(obj);
    m_Directive.Set8085Object(obj);
}


//////////////////////////////////////////////////////////////////////
// if the data in BaseLex::m_szToken is a valid number then return its value
// else check to see if it is a symbol defined by one of the equ statements
// if so return its value.  if bThrowException(default= true) is true then
// throw an exception 
word CParse::GetNumber(char *str,int base, BOOL bThrowException)
{
    return ParseExpression(str,base,bThrowException);
}

word CParse::GetNumber(BOOL bThrowException)
{
    int nRet = Advance();
    return GetNumber(GetToken(),nRet,bThrowException);
}

//////////////////////////////////////////////////////////////////////
//
void CParse::Init()
{
    m_bEof = FALSE;
    m_bProcessingFile = FALSE;
    m_InFile = NULL;
    Set8085Object(NULL);
    m_szTemporary = new char[BaseLex::GetBufferLen()];
    m_Ptr = 0;
}

//////////////////////////////////////////////////////////////////////
//
void CParse::Close()
{
    m_szFileName[0] = '\0';
    if(m_bProcessingFile)
    {
        fclose(m_InFile);
        m_InFile = NULL;
        m_cDebugData.Close();
        m_bProcessingFile = FALSE;
    }
}

//////////////////////////////////////////////////////////////////////
//
CString
CParse::GetErrorPrefix()
{
    m_szErrorPrefix.Format("Line - %d Column - %d ",GetLineNumber(),GetColumn());
    if(::nDebugPrefix)
        m_szErrorPrefix = "Parse " + m_szErrorPrefix;
    return m_szErrorPrefix;
}

//////////////////////////////////////////////////////////////////////
//
int CParse::Open( const CString& fileName)
{
    strcpy(m_szFileName,fileName);
    m_InFile = OpenFile(fileName);

    // now open the file containing debug information.
    strcpy(m_szDebugFileName,fileName);
    strcat(m_szDebugFileName,".debug");
    m_cDebugData.Open(m_szDebugFileName,"wt");
    m_bProcessingFile = TRUE;
    m_bEof = FALSE;
    return 1;
}

FILE *CParse::OpenFile( const CString& szFileName,char *mode)
{
    FILE *fp;
    if(szFileName.IsEmpty())
    {
        ThrowException(severe,empty_file_name,"Empty file name");
    }
    if(NULL==(fp=fopen(szFileName,mode)))
    {
        //m_CException.Set(severe,file_open_error,"Error could not open file");
        //throw m_CException;
        ThrowException(severe,file_open_error,"Error could not open file");
    }
    return fp;
}

//////////////////////////////////////////////////////////////////////
// return value of 0 means success
int CParse::Parse(char *str)
{
    int nRet=0;

    for(SetBuffer(str), nRet=Advance() ; nRet != EOLN ; nRet = Advance())
    {
        ParseRemainingLine(nRet);
    }
    return nRet;
}

//////////////////////////////////////////////////////////////////////
// ParseFile is Called once to parse an entire file
// return value of 0 means success
int CParse::ParseFile(const CString& fileName)
{
    Reset();
    try
    {
        Open(fileName);
        ASSERT(m_bProcessingFile);
        do
        {
            Parse(BaseLex::GetBuffer());
        }
        while((!m_bEof));
        ResolveUnresolvedSymbols();
        Close();
    }
    catch (CMyException& e)
    {
        Close();
        m_CException = e;
        throw m_CException;
    }
    catch (CMyException *e)
    {
        Close();
        m_CException = *e;
        delete e;
        throw m_CException;
    }
    return 0;
}

//////////////////////////////////////////////////////////////////////
// return value of 0 means success
int CParse::ParseRemainingLine(int type)
{
    switch(type)
    {
    case INSTRUCTION    :   
        return ParseInstruction();

    case DIRECTIVE      :   
        if(!m_Directive.IsDirective(GetToken()))
            ThrowException(severe,illegal_directive,"Illegal directive");
        return ParseDirective();

    case LABEL          :   
        if(m_SymbolTable.Search(GetToken()))
        {
            ThrowException(severe,duplicate_symbol,"Duplicate Symbol");
        }

        if(!m_SymbolTable.Add(GetToken(),m_Ptr))
        {
            ThrowException(severe,table_overflow,"Internal error - Symbol Table overflow");
        }

        break;

    case COMMENT        :   break;

    case IDENTIFIER     :
    default             :   
        ThrowException(severe,syntax_error,"Syntax Error");
    }
    return 0;
}

//////////////////////////////////////////////////////////////////////
// 
int CParse::ParseDirective()
{
    int nRet;
    switch(m_Directive.GetGenericType())
    {
    case equ            :
        strcpy(m_szTemporary,GetIdentifier());
        nRet = Advance();
        if(IS_NUMBER(nRet))
        {
            if(m_SymbolTable.Search(m_szTemporary))
            {
                ThrowException(severe,duplicate_symbol,"Duplicate Symbol");
            }
            if(!m_SymbolTable.Add(m_szTemporary,
            (word)(AsciiToNum(BaseLex::GetToken(),nRet))))
            {
                ThrowException(severe,table_overflow,"Internal error - Symbol Table overflow");
            }
        }
        return 0;

    case begin      :  
    case org        :  
    case fill       :
    case setbyte    :
    case setword    :
        // get the numeric equivalent of the token in BaseLex::m_szToken
        if(m_Directive.GetGenericType()==org)
            m_Ptr = GetNumber();
        else if(m_Directive.GetGenericType()==begin)
            Get8085Object()->SetStartIP(GetNumber());
        else if(m_Directive.GetGenericType()==setbyte)
        {
            word address = GetNumber();
            SkipComma();
            byte data = (byte)GetNumber();
            Get8085Object()->WriteMem(address,data);
        }
        else if(m_Directive.GetGenericType()==setword)
        {
            word address = GetNumber();
            SkipComma();
            word data = (word)GetNumber();
            Get8085Object()->WriteMem(address,data);
        }
        else
        {
            word address1 = GetNumber();
            SkipComma();
            word address2 = GetNumber();
            SkipComma();
            byte data  = byte(GetNumber());
            for(word i=address1; i<address2 ; i++)
                Get8085Object()->WriteMem(i,data);
        }
        return 0;
    }
    ThrowException(severe,unknown,"Should never reach here CParse::ParseDirective");
    return 1;
}

//////////////////////////////////////////////////////////////////////
// 
int CParse::Advance()
{
    if(!m_bProcessingFile) return Lex::Advance();
    if( BaseLex::GetUnparsedString()==NULL)
    {
        if(Read(m_InFile)==NULL)
                m_bEof=TRUE;
    }
    return Lex::Advance();
}

//////////////////////////////////////////////////////////////////////
// 
int CParse::IsInstruction(char *str)
{
    return m_InstrTable.Search(str)==-1 ? 0 : 1;
}

//////////////////////////////////////////////////////////////////////
// 
int CParse::ParseInstruction()
{
    WriteDebugInfo();
    switch(m_InstrTable.GetSpecificType())
    {

    case aci    :
    case adi    :
    case ani    :
    case cpi    :
    case in     :
    case ori    :
    case out    :
    case sbi    :
    case sui    :
    case xri    :
        InsertCodeAnd8Bit(); // expect 8 bit data as operand 
        break;

    case mvi    :
        InsertRegisterAnd8Bit(); // expect reg,8 bit data as operand 
        break;

    case lxi    :
        InsertRegisterAnd16Bit(regs(b)|regs(d)|regs(h)|regs(sp)); // expect reg,8 bit data as operand 
        break;

    case rst    :
        InsertRst();
        break;

    case adc    :
    case add    :
    case ana    :
    case cmp    :
    case ora    :
    case sbb    :
    case sub    :
    case xra    :
        InsertRegister(); // expect a reg or memory as operand 
        break;

    case dcr    :
    case inr    :
        InsertRegister(1,8); // expect a reg or memory as operand 
        break;

    case dad    :
    case dcx    :
    case inx    :
        // register pairs only
        InsertRegister(regs(b)|regs(d)|regs(h)|regs(sp),8);
        break;

    case pop    :
    case push   :
        // register pairs only
        InsertRegister(regs(b)|regs(d)|regs(h)|regs(psw),8);
        break;

    case stax   :
    case ldax   :
        // register pairs only 
        InsertRegister(regs(b)|regs(d));
        break;

    case mov    :
        Insert2Registers();  /* expects 2 registers/memory as operand */
        break;

    case call   :
    case cc     :
    case cnc    :
    case cp     :
    case cm     :
    case cpe    :
    case cpo    :
    case cz     :
    case cnz    :

    case jmp    :
    case jc     :
    case jnc    :
    case jp     :
    case jm     :
    case jpe    :
    case jpo    :
    case jz     :
    case jnz    :
    case lda    :
    case lhld   :
    case shld   :
    case sta    :
        InsertCodeAnd16Bit(); /* expect 16 bit data as operand */
        break;

    case cma    :
    case cmc    :
    case daa    :
    case di     :
    case ei     :
    case hlt    :
    case nop    :
    case pchl   :
    case ral    :
    case rar    :
    case rlc    :
    case rrc    :
    case ret    :
    case rc     :
    case rnc    :
    case rp     :
    case rm     :
    case rpe    :
    case rpo    :
    case rz     :
    case rnz    :
    case rim    :
    case sim    :
    case sphl   :
    case stc    :
    case xchg   :
    case xthl   :
        InsertCode();       /*does not expect any operands */
        break;

    default     :
        ThrowException(severe,syntax,"Unknown error in Parser [ParseInstruction]");
        break;
    }
    // change the value of IP with the length of the instruction
    //Get8085Object()->SetPtr(m_Ptr+m_InstrTable.GetInstructionLen());

    m_Ptr += m_InstrTable.GetInstructionLen();
    return 0;
}

//////////////////////////////////////////////////////////////////////
// 
regs CParse::IdentifyRegister(char * str,int dont_validate)
{
    regs r;

    switch(tolower(*str))
    {
    case 'a'    :   r = (a); break;
    case 'b'    :   r = (b); break;
    case 'c'    :   r = (c); break;
    case 'd'    :   r = (d); break;
    case 'e'    :   r = (e); break;
    case 'h'    :   r = (h); break;
    case 'l'    :   r = (l); break;
    case 'm'    :   r = (m); break;
    default     :   
        if(strcmpi(str,"sp")==0)
            r = (sp);
        else if(strcmpi(str,"psw")==0)
            r = (psw);
        else 
            ThrowException(severe,illegal_register,"Illegal register specified");
        goto checkValidRegs;
    }
    // if its register represented by a single character then str[1] must be '0'
    if(str[1]!='\0')
        ThrowException(severe,illegal_register," Illegal register");

checkValidRegs:
    if(dont_validate==1) return r;
    if(!( r & dont_validate ))
        ThrowException(severe,illegal_register," Illegal register");
    return r;
}

//////////////////////////////////////////////////////////////////////
// 
regs CParse::IdentifyRegister(int validRegs)
{
    // advance to the next token and identify if it is a register
    return IdentifyRegister(GetIdentifier(),validRegs);
}

//////////////////////////////////////////////////////////////////////
// 
regs CParse::IdentifyRegisterPair(char * str,int validRegs)
{
    regs r;

    if((r=IdentifyRegister(str))!=b && r!=d && r!=h && r!=sp && r!=psw)
        ThrowException(severe,illegal_register_pair,"Illegal register pair");
    return r;
}

//////////////////////////////////////////////////////////////////////
// 
regs CParse::IdentifyRegisterPair(int validRegs)
{
    return IdentifyRegisterPair(GetIdentifier(),validRegs);
}

//////////////////////////////////////////////////////////////////////
// 
int CParse::RegToNumber(regs r)
{
    switch(r)
    {
    case a      : return 7;
    case b      : return 0;
    case c      : return 1;
    case d      : return 2;
    case e      : return 3;
    case h      : return 4;
    case l      : return 5;
    case m      : return 6;
    case sp     : return 6;
    case psw    : return 6;
    }
    ThrowException(severe,illegal_register,"Illegal register supplied");
    return 0;
}

int CParse::RegToNumber(regs r1, regs r2)
{
    return RegToNumber(r1)*8 + r2;
}

int CParse::AsciiToNum(char * str, int base)
{
    char *endptr;
    return strtol(str,&endptr,base);
}

//////////////////////////////////////////////////////////////////////
// 
char * CParse::GetIdentifier(BOOL bThrowException)
{
    if((Advance()!=IDENTIFIER) && (bThrowException))
    {
        ThrowException(severe,number_or_identifier_expected,
            " Syntax Error: Expecting an identifier");
    }
    return GetToken();
}

//////////////////////////////////////////////////////////////////////
// 

void CParse::SkipComma(BOOL bThrowException)
{
    if((Advance()!=COMMA) && bThrowException)
    {
        ThrowException(severe,comma_expected," Syntax Error: Expecting an identifier");
    }

}

//////////////////////////////////////////////////////////////////////
// expects one register and 16 bit number as operand
// eg: lxi h,5678
void CParse::InsertRegisterAnd16Bit(int validRegs)
{
    regs r = IdentifyRegister(validRegs); // get the next token as a register
    SkipComma();
    byte opcode = (byte)(m_InstrTable.GetOpcode()+8*RegToNumber(r));
    Get8085Object()->InsertMachineCode(m_Ptr,opcode,GetNumber());
}

//////////////////////////////////////////////////////////////////////
// expects an 8 bit number as operand
void CParse::InsertRegisterAnd8Bit(int validRegs)
{
    regs r = IdentifyRegister(validRegs); // get the next token as a register
    SkipComma();
    byte opcode = (byte)(m_InstrTable.GetOpcode()+8*RegToNumber(r));
    Get8085Object()->InsertMachineCode(m_Ptr,opcode,(byte)GetNumber());
}

//////////////////////////////////////////////////////////////////////
// expects one register as operand
void CParse::InsertRegister(int validRegs,int nFactor)
{
    // get the next token as a register
    regs r = IdentifyRegister(validRegs);
    byte opcode = (byte)(m_InstrTable.GetOpcode()+nFactor*RegToNumber(r));
    Get8085Object()->InsertMachineCode(m_Ptr,opcode);
}

//////////////////////////////////////////////////////////////////////
// expects one register as operand
void CParse::Insert2Registers(int validRegs)
{
    // get the next token as a register
    regs r1 = IdentifyRegister(validRegs);
    SkipComma();
    regs r2 = IdentifyRegister(validRegs);
    if(r1==m && r2==m)
        ThrowException(severe,illegal_register,
                " Both operands cannot be memory location ");
    byte opcode = (byte)(m_InstrTable.GetOpcode()+8*RegToNumber(r1));
    Get8085Object()->InsertMachineCode(m_Ptr,opcode+byte(RegToNumber(r2)));
}

//////////////////////////////////////////////////////////////////////
// expects one register pair as operand
void CParse::InsertRegisterPair(int validRegs)
{
    // get the next token as a register
    regs r = IdentifyRegisterPair(validRegs); 
    byte opcode = (byte)(m_InstrTable.GetOpcode()+RegToNumber(r));
    Get8085Object()->InsertMachineCode(m_Ptr,opcode);
}

//////////////////////////////////////////////////////////////////////
// expects 16 bit number
void CParse::InsertCodeAnd16Bit(int validRegs)
{
    // get the next token as a number
    word number = GetNumber(FALSE);
    byte opcode = (byte)(m_InstrTable.GetOpcode());
    Get8085Object()->InsertMachineCode(m_Ptr,opcode,number);
}

//////////////////////////////////////////////////////////////////////
// does not expect any operands
void CParse::InsertCode()
{
    Get8085Object()->InsertMachineCode(m_Ptr,(byte)(m_InstrTable.GetOpcode()));
}


void CParse::InsertCodeAnd8Bit()
{
    byte number = (byte)(GetNumber()&0xff);
    Get8085Object()->InsertMachineCode(m_Ptr,m_InstrTable.GetOpcode(),number);
}

void CParse::InsertRst()
{
    word number = GetNumber();
    if(number>7) 
        ThrowException(severe,illegal_rst,"Illegal RST instruction");
    Get8085Object()->InsertMachineCode(m_Ptr,m_InstrTable.GetOpcode()+8*number);
}

void CParse::ResolveUnresolvedSymbols()
{
    for(int i=0; i<m_UnresolvedJumps.GetSize(); i++)
    {
        // enhance - also display line number in the source code
        char *symbol = m_UnresolvedJumps.GetAt(i)->m_szSymbol;// get the label

        
        if(!m_SymbolTable.Search(symbol))  // search in the symboltable
        {
            sprintf(m_szTemporary,"Unresolved symbol <%s>",symbol);
			// this has to be handled differently otherewise the 
			// line and column number reported will be the end of file
		    m_szErrorPrefix.Format("Line - %d Column - %d  %s",
				m_UnresolvedJumps.GetAt(i)->m_nSourceLine,
				m_UnresolvedJumps.GetAt(i)->m_nSourceColumn,m_szTemporary);
            if(::nDebugPrefix)
                m_szErrorPrefix = "Parse " + m_szErrorPrefix;
			throw CMyException(severe,unresolved_symbol,m_szErrorPrefix);
            //ThrowException(severe,unresolved_symbol,m_szTemporary);
        }

        word data= m_SymbolTable.GetLastSuccessfulLookup();
        for(int j=0 ; j<m_UnresolvedJumps.GetAt(i)->m_Addressess.GetSize() ; j++)
        {
            word address = m_UnresolvedJumps.GetAt(i)->m_Addressess.GetAt(j);
            Get8085Object()->WriteMem(address+1,data);
        }
    }
}

void CParse::AddUnresolvedJump(char * str, word addr)
{
    for(int i=0; i<m_UnresolvedJumps.GetSize() ; i++)
        if(strcmpi(m_UnresolvedJumps.GetAt(i)->m_szSymbol,str)==0)
        {
            m_UnresolvedJumps.GetAt(i)->m_Addressess.Add(addr);
            return;
        }
        Unresolved *u= new Unresolved;
        strcpy(u->m_szSymbol,str);
		// this is line number reported when the symbol cannot be found
		// at the end of parsing the entire file
		u->m_nSourceLine = GetLineNumber(); 
		u->m_nSourceColumn = GetColumn();
        u->m_Addressess.Add(addr);
        m_UnresolvedJumps.Add(u);
}

int CParse::ParseAndInsertAt(word addr,char *str)
{
    int nRet = 0;
    Reset();
    m_Ptr = addr;
    return Parse(str);
    try
    {
        for(SetBuffer(str), nRet=Advance() ; nRet != EOLN ; nRet = Advance())
        {
                ParseRemainingLine(nRet);
        }
    }
    catch (CMyException *e)
    {
        m_CException = *e;
        delete e;
        throw m_CException;
    }
    catch (CMyException e)
    {
        throw;
    }
    return nRet;
}

void CParse::Reset()
{
    m_SymbolTable.Reset(); 
    Lex::Init(); 
    BaseLex::Init();
    m_Ptr = 0;
    for(int i=0; i<m_UnresolvedJumps.GetSize(); i++)
        delete m_UnresolvedJumps.GetAt(i);
	m_UnresolvedJumps.RemoveAll();
}


void CParse::WriteDebugInfo()
{
    if(m_bProcessingFile)
        m_cDebugData.Write(m_Ptr,GetLineNumber());
}

char * CParse::GetDebugFileName()
{
    return m_szDebugFileName;
}

word CParse::ParseExpression(char *str, int nBase, BOOL bThrowException)
{
    // enhance, - add ability to parse expression 
    // eg:-
    // .equ data 3
    // .setbyte data,4
    // .setbyte data+1,7
    // The trouble starts when a forward declared identifier is referred
    // in an expression.
    if(nBase!=IDENTIFIER && nBase!=HEX && nBase!=DECIMAL && nBase!=OCTAL && nBase!=BINARY)
    {
        ThrowException(severe,number_expected," Expecting number or constant symbol");
    }

    if(nBase==IDENTIFIER)
    {
        if(m_SymbolTable.Search(str))
            return (word)m_SymbolTable.GetLastSuccessfulLookup();
        // This symbol was not found hence add it to the list of unresolved symbols
        AddUnresolvedJump(str,m_Ptr);
        if(bThrowException)
        {
            ThrowException(severe,number_expected," Expecting number or constant symbol");
        }
        return 0; // the symbol was not found in the symbol table 
    }
    return AsciiToNum(str,nBase);
}
